#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>

#define N 128

#define ERR_LOG(errmsg) do{\
                             perror(errmsg);\
                             exit(1);\
                          }while(0)

int main(int argc,char const *argv[]){
    if(argc < 3){
        fprintf(stderr,"Usage: %s [ip] [port]\n",argv[0]);
	exit(1);
    }

    //第一步：创建套接字
    int sockfd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){
       ERR_LOG("fail to socket");
    }

    //将套接字设置为允许重复使用本机地址(因为没有多台设备充当客户端)或者设置为端口复用
    int on=1;
    if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0){
       ERR_LOG("fail to setsockopt");
    }

    //第二步：将套接字与服务器网络信息结构体绑定
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    
    if(bind(sockfd,(struct sockaddr *)&serveraddr,addrlen)==-1){
        ERR_LOG("fail to bind");
    }

    //第三步：将套接字设置为被动监听状态
    if(listen(sockfd,10)==-1){
        ERR_LOG("fail to listen");
    }

    //第四步：阻塞等待客户端的连接请求
    int acceptfd;//这个变量用来接收accept()返回的套接字，每个套接字代表与一个客户端的连接
    struct sockaddr_in clientaddr;
    if((acceptfd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen))==-1){
        ERR_LOG("fail to accept");
    }

    //打印连接的客户端的信息
    printf("ip:%s,port:%d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));

    //第五步：进行通信
    //tcp服务器与客户端通信时，需要使用accept函数的返回值
    char buf[N] = "";
    ssize_t bytes;
    while(1){
        if((bytes = recv(acceptfd,buf,N,0))<0)
	{
		ERR_LOG("fail to recv");
	}
	else if(bytes == 0)
	{
		printf("The client quited\n");
		exit(1);
	}

        if(strncmp(buf,"quit",4)==0)
	{
		exit(0);
	}

	printf("from client: %s\n",buf);

	strcat(buf,"*_*");//将参数1和参数2拼接，并将结果返回到参数1中。

	if(send(acceptfd,buf,N,0)<0)
	{
		ERR_LOG("fail to send");
	}
    }

    //关闭套接字文件描述符
    close(acceptfd);//关闭通信套接字
    close(sockfd);//关闭连接套接字

    return 0;
}

